
//---------------------------------------------------------------------------
#include "rendering/pg1_renderer.h"
//---------------------------------------------------------------------------
#include "pg1_vertexbuffer.h"
//---------------------------------------------------------------------------
#include <d3dx9.h>
//---------------------------------------------------------------------------
using namespace pg1;
//---------------------------------------------------------------------------
// Engine to DirectX primitives enum mapping
D3DPRIMITIVETYPE g_ePrimitivesMapping[PrimitiveCount] = 
{
	D3DPT_TRIANGLELIST,		// TriangleList
	D3DPT_TRIANGLESTRIP,	// TriangleStrip
	D3DPT_POINTLIST,		// PointList
    D3DPT_LINELIST,			// LineList
    D3DPT_LINESTRIP,		// LineStrip
	D3DPT_TRIANGLEFAN		// TriangleFan
};
//---------------------------------------------------------------------------
// Engine to DirectX matrix type enum mapping
D3DTRANSFORMSTATETYPE g_eMatrixTypeMapping[MatrixTypeCount] = 
{
	D3DTS_VIEW,			// View
	D3DTS_PROJECTION,	// Projection
	D3DTS_WORLD			// World
};
//---------------------------------------------------------------------------
Renderer::Renderer ()
:
m_pkColorVB(NULL),
m_pkD3D(NULL),
m_pkDevice(NULL)
{
	// nothing to do
}
//---------------------------------------------------------------------------
Renderer::~Renderer ()
{
	// delete vertex buffers
	delete m_pkColorVB;
	m_pkColorVB = NULL;

	delete m_pkTexCoordVB;
	m_pkTexCoordVB = NULL;

	// delete textures
	for( std::vector<Texture>::iterator it = m_akTextures.begin(); 
		 it != m_akTextures.end(); 
		 it++ )
	{
		(*it)->Release();
		(*it) = NULL;
	}

	m_akTextures.clear();
}
//---------------------------------------------------------------------------
bool Renderer::init (HWND hWnd)
{
	m_pkD3D = Direct3DCreate9(D3D_SDK_VERSION);

	if(!m_pkD3D)
	{
		return false;
	}

	D3DDISPLAYMODE displayMode;
	HRESULT hr;

	hr = m_pkD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode);

	if(hr != D3D_OK)
	{
		return false;
	}

	D3DPRESENT_PARAMETERS d3DPresentParameters;
	ZeroMemory(&d3DPresentParameters, sizeof(d3DPresentParameters));

	d3DPresentParameters.BackBufferFormat = displayMode.Format;
	d3DPresentParameters.Windowed = true;
	d3DPresentParameters.BackBufferCount = 1; 
	d3DPresentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3DPresentParameters.hDeviceWindow = 0; 
	d3DPresentParameters.Flags = 0;
	d3DPresentParameters.EnableAutoDepthStencil = TRUE;
	d3DPresentParameters.AutoDepthStencilFormat = D3DFMT_D16;
	d3DPresentParameters.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	d3DPresentParameters.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

	hr = m_pkD3D->CreateDevice(
							  D3DADAPTER_DEFAULT,
 							  D3DDEVTYPE_HAL,							
							  hWnd,
							  D3DCREATE_HARDWARE_VERTEXPROCESSING,
							  &d3DPresentParameters,
							  &m_pkDevice
	);

	if(hr != D3D_OK)
	{
		return false;
	}

	// disable lighting
	m_pkDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
	m_pkDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	//m_pkDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);

	// enable blending
	m_pkDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	m_pkDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
	m_pkDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	m_pkDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

	// initialize projection matrix
	D3DVIEWPORT9 kViewport;
	m_pkDevice->GetViewport(&kViewport);

	float fViewportWidth = static_cast<float>(kViewport.Width);
	float fViewportHeight = static_cast<float>(kViewport.Height);

	// perspective or orthogonal examples
	D3DXMATRIX kProjectionMatrix;
	D3DXMatrixOrthoLH(&kProjectionMatrix, fViewportWidth, fViewportHeight, -1.0f, 1.0f);
	m_pkDevice->SetTransform(D3DTS_PROJECTION, &kProjectionMatrix);

	// create VBs
	m_pkColorVB = new VertexBuffer(m_pkDevice, sizeof(ColorVertex), pg1::ColorVertexType);
	m_pkTexCoordVB = new VertexBuffer(m_pkDevice, sizeof(TexCoordVertex), pg1::TexCoordVertexType);

	return true;
}
//---------------------------------------------------------------------------
void Renderer::beginFrame ()
{
	m_pkDevice->BeginScene();
	m_pkDevice->Clear(0, 
					  NULL, 
					  D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 
					  D3DCOLOR_XRGB(0,0,255),		// background color
					  1.0f, 
					  0
	);
}
//---------------------------------------------------------------------------
void Renderer::endFrame ()
{
	m_pkColorVB->flush();

	m_pkDevice->EndScene();
	m_pkDevice->Present(NULL, NULL, NULL, NULL);
}
//---------------------------------------------------------------------------
void Renderer::draw (ColorVertex* apkVertices, Primitive ePrimitive, size_t uiVertexCount)
{
	m_pkColorVB->bind();
	m_pkColorVB->draw(apkVertices, 
					  g_ePrimitivesMapping[ePrimitive], 
					  uiVertexCount);
}
//---------------------------------------------------------------------------
void Renderer::draw (TexCoordVertex* apkVertices, Primitive ePrimitive, size_t uiVertexCount)
{
	m_pkTexCoordVB->bind();
	m_pkTexCoordVB->draw(apkVertices, 
						 g_ePrimitivesMapping[ePrimitive], 
						 uiVertexCount);
}
//---------------------------------------------------------------------------
void Renderer::setMatrix (MatrixType eMatrixType, const Matrix& rkMatrix)
{
	m_pkDevice->SetTransform( g_eMatrixTypeMapping[eMatrixType], rkMatrix );
}
//---------------------------------------------------------------------------
const Texture Renderer::loadTexture (const std::string& rkFilename, int iColorKey)
{
	IDirect3DTexture9* pkTexture = NULL;
	HRESULT hr = D3DXCreateTextureFromFileEx(
											m_pkDevice,
											rkFilename.c_str(), 
											0, 0, 0, 0,
											D3DFMT_UNKNOWN, D3DPOOL_MANAGED,
											D3DX_FILTER_NONE, D3DX_FILTER_NONE,
											iColorKey,
											NULL,
											NULL,
											&pkTexture
	);

	if (hr != D3D_OK)
	{
		return NoTexture;
	}
	else
	{
		m_akTextures.push_back(pkTexture);
		return pkTexture;
	}
}
//---------------------------------------------------------------------------
void Renderer::setCurrentTexture (const Texture& rkTexture)
{
	m_pkDevice->SetTexture(0, rkTexture);
}
//---------------------------------------------------------------------------
